package com.hyt.it.ogt.kq.service.bm.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.common.utils.result.Result;
import com.hyt.core.base.BaseEntity;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.constant.CommonConstant;
import com.hyt.core.enums.DelFlagEnum;
import com.hyt.core.enums.NumberEnum;
import com.hyt.core.util.UUIDUtils;
import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.it.ogt.cj.enums.ComponentNameEnum;
import com.hyt.it.ogt.cj.model.entity.ProjectCollectInfo;
import com.hyt.it.ogt.cj.model.entity.ProjectCollectInfoOption;
import com.hyt.it.ogt.cj.model.entity.ProjectImageStandard;
import com.hyt.it.ogt.cj.model.vo.ProjectCollectInfoVo;
import com.hyt.it.ogt.cj.service.IProjectCollectInfoOptionService;
import com.hyt.it.ogt.cj.service.IProjectCollectInfoService;
import com.hyt.it.ogt.cj.service.IProjectImageStandardService;
import com.hyt.it.ogt.kq.common.bm.enums.BmProjectStateEnum;
import com.hyt.it.ogt.kq.common.bm.enums.NodeTypeEnum;
import com.hyt.it.ogt.kq.common.bm.enums.ProjectOpenEnum;
import com.hyt.it.ogt.kq.common.bm.enums.StuStatusEnum;
import com.hyt.it.ogt.kq.common.bm.utils.Constant;
import com.hyt.it.ogt.kq.common.bm.utils.DateUtil;
import com.hyt.it.ogt.kq.common.config.ConfigManager;
import com.hyt.it.ogt.kq.common.config.exception.InvalidRequestException;
import com.hyt.it.ogt.kq.common.utils.KqStringUtils;
import com.hyt.it.ogt.kq.service.bm.command.WeChatMiniNotifyPushCommand;
import com.hyt.it.ogt.kq.service.bm.feign.file.FileClient;
import com.hyt.it.ogt.kq.service.bm.feign.kw.KwClient;
import com.hyt.it.ogt.kq.service.bm.feign.tk.TkClient;
import com.hyt.it.ogt.kq.service.bm.feign.tps.TpsClient;
import com.hyt.it.ogt.kq.service.bm.mapper.NodeMapper;
import com.hyt.it.ogt.kq.service.bm.mapper.ProjectMapper;
import com.hyt.it.ogt.kq.service.bm.mapper.StudentProjectMapper;
import com.hyt.it.ogt.kq.service.bm.model.entity.Project;
import com.hyt.it.ogt.kq.service.bm.model.entity.ProjectNode;
import com.hyt.it.ogt.kq.service.bm.model.entity.ProjectStatistic;
import com.hyt.it.ogt.kq.service.bm.model.entity.ProjectSubject;
import com.hyt.it.ogt.kq.service.bm.model.entity.StudentProject;
import com.hyt.it.ogt.kq.service.bm.model.entity.StudentSubject;
import com.hyt.it.ogt.kq.service.bm.model.vo.FirstStatVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectCopyVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectNodeVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectParam;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectPreviewVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectStatVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectSubjectVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.ProjectVo;
import com.hyt.it.ogt.kq.service.bm.model.vo.SimpleProjectVO;
import com.hyt.it.ogt.kq.service.bm.model.vo.StudentProjectInfoVo;
import com.hyt.it.ogt.kq.service.bm.mq.ProjectStatisticProducer;
import com.hyt.it.ogt.kq.service.bm.service.INonOpenService;
import com.hyt.it.ogt.kq.service.bm.service.IProject2Service;
import com.hyt.it.ogt.kq.service.bm.service.IProjectExamService;
import com.hyt.it.ogt.kq.service.bm.service.IProjectNodeService;
import com.hyt.it.ogt.kq.service.bm.service.IProjectService;
import com.hyt.it.ogt.kq.service.bm.service.IProjectStatisticService;
import com.hyt.it.ogt.kq.service.bm.service.IProjectSubjectService;
import com.hyt.it.ogt.kq.service.bm.service.IStudentInfoService;
import com.hyt.it.ogt.kq.service.bm.service.IStudentPayService;
import com.hyt.it.ogt.kq.service.bm.service.IStudentProjectService;
import com.hyt.it.ogt.kq.service.bm.service.IStudentSubjectService;
import com.hyt.it.ogt.kq.service.bm.service.IUserPermissionService;
import com.hyt.it.ogt.kq.service.gov.api.bm.BmApi;
import com.hyt.it.ogt.kq.service.model.vo.wechat.PushExamNotifyDTO;
import com.hyt.loginfo.model.User;
import com.hyt.loginfo.service.IBaseLoginService;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 报名项目 服务实现类
 * </p>
 *
 * @author wangt
 * @since 2020-05-15
 */
@Slf4j
@Service
public class ProjectServiceImpl extends BaseServiceImpl<ProjectMapper, Project> implements IProjectService {

    @Resource
    RedisTemplate<String, Object> redisTemplate;
    @Resource
    IProjectSubjectService projectSubjectService;
    @Resource
    IProjectCollectInfoService projectCollectInfoService;
    @Resource
    IProjectCollectInfoOptionService iProjectCollectInfoOptionService;
    @Resource
    IProjectImageStandardService iProjectImageStandardService;
    @Resource
    IStudentProjectService iStudentProjectServiceService;
    @Resource
    IStudentSubjectService iStudentSubjectServiceService;
    @Resource
    IStudentInfoService iStudentInfoServiceService;
    @Resource
    IProjectExamService iProjectExamService;
    @Resource
    ProjectMapper projectMapper;
    @Resource
    private ConfigManager configManager;
    @Resource
    private KwClient kwClient;
    @Resource
    private IUserPermissionService iUserPermissionService;
    @Resource
    private NodeMapper nodeMapper;
    @Resource
    private TkClient tkClient;
    @Resource
    private TpsClient tpsClient;
    @Resource
    private BmApi kwGovClient;
    @Resource
    private IBaseLoginService iBaseLoginService;
    @Resource
    private IProjectNodeService iProjectNodeService;
    @Resource
    private IProjectStatisticService projectStatisticService;
    @Resource
    private ProjectStatisticProducer projectStatisticProducer;

    @Value("${hwcloud.fileuploadpath}")
    private String hwcloudPath;

    @Resource
    private IProject2Service iProject2Service;
    @Resource
    private IStudentPayService iStudentPayService;
    @Resource
    private IStudentInfoService iStudentInfoService;

    @Resource
    private IProjectCollectInfoService iProjectCollectInfoService;
    @Resource
    INonOpenService iNonOpenService;
    @Resource
    StudentProjectMapper studentProjectMapper;
    @Resource
    FileClient fileClient;

    /**
     * @Params:
     * @Description: 项目报名统计
     * @Author: STARF
     * @CreateDate: 2022/3/15 9:24
     **/
    public ProjectStatVo getProjectStat(String projectId, Integer isOpen) {
        String deptId = "";
        if (0 == isOpen) {
            deptId = iBaseLoginService.getDeptId();
        }
        List<String> permissionList = new ArrayList<String>();
        boolean flag = iUserPermissionService.needCheckAreaPermission(projectId);
        if (flag) {
            permissionList = iUserPermissionService.queryPermissionByUserId(projectId, iBaseLoginService.getUserId());
            if (permissionList != null && permissionList.size() == 0) {
                permissionList = new ArrayList<String>();
                //如果是无权限，设置一个不存在的权限
                permissionList.add("NoPermission");
            }
        }
        return this.statProjectStudent(projectId, permissionList, deptId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveProjectSubject(ProjectVo projectVo) {
        projectVo.setPtBangFlag(0);
        if (StringUtils.isEmpty(projectVo.getId())) {
            projectVo.setId(UUIDUtils.newSortUUID());
        } else {
            Project project = this.getById(projectVo.getId());
            if (null != project) {
                projectVo.setPtBangFlag(project.getPtBangFlag());
            }
        }

        Project project = new Project();
        BeanUtils.copyProperties(projectVo, project);

        // 非开放型报名必须要有证件采集字段
        valiateProjectOpenStatus(projectVo);

        //报名科目数量校验
        validateProjectSubjectsTimes(projectVo);

        //项目项目名称校验
        validateProjectNameUsered(projectVo);

        // 校验项目的流程节点
        validateProjectNodes(projectVo);

        // 保存项目
        saveProject(project);

        // 保存科目
        saveProSubject(projectVo);

        // 保存节点信息
        saveProjectNode(projectVo.getNodeList(), project);
    }

    private void validateProjectNodes(ProjectVo projectVo) {
        // 保存流程配置节点信息
        if (null != projectVo.getIsNotice() && projectVo.getIsNotice() == 1) {
            if (ArrayUtil.isEmpty(projectVo.getNodeList()) || StringUtils.isEmpty(projectVo.getNodeList().get(0).getNodeTitle())
                    || StringUtils.isEmpty(projectVo.getNodeList().get(0).getNodeContent())) {
                throw new InvalidRequestException(Constant.RESPONSE_CODE_31051112);
            }
        }
    }

    private void validateProjectNameUsered(ProjectVo projectVo) {
        QueryWrapper<Project> wrapper = new QueryWrapper<Project>();
        wrapper.eq("office_id", projectVo.getOfficeId());
        wrapper.eq("project_name", projectVo.getProjectName());
        if (StringUtils.isNotEmpty(projectVo.getId())) {
            wrapper.ne("id", projectVo.getId());
        }
        List<Project> tempProject = this.list(wrapper);
        if (tempProject != null && tempProject.size() > 0) {
            throw new InvalidRequestException(Constant.RESPONSE_CODE_31010143);
        }
    }

    private void validateProjectSubjectsTimes(ProjectVo projectVo) {
        if (ArrayUtil.isEmpty(projectVo.getSubjectList())) {
            throw new InvalidRequestException(Constant.RESPONSE_CODE_31051109);
        }
        if (projectVo.getMaxSubject() != null) {
            if (projectVo.getMaxSubject() > projectVo.getSubjectList().size()) {
                throw new InvalidRequestException(Constant.RESPONSE_CODE_31051110);
            }
            if (projectVo.getMaxSubject() <= 0) {
                throw new InvalidRequestException(Constant.RESPONSE_CODE_31051110);
            }
        }
        if (projectVo.getMinSubject() != null) {
            if (projectVo.getMinSubject() > projectVo.getSubjectList().size()) {
                throw new InvalidRequestException(Constant.RESPONSE_CODE_31051110);
            }
            if (projectVo.getMinSubject() <= 0) {
                throw new InvalidRequestException(Constant.RESPONSE_CODE_31051110);
            }
        }
        for (ProjectSubject projectSubject : projectVo.getSubjectList()) {
            if (NumberUtil.equals(projectSubject.getIsPay(), CommonConstant.ONE)) {
                if (projectSubject.getPrice().doubleValue() < 0.01) {
                    throw new InvalidRequestException(31051113, "科目缴费金额必须大于等于0.01元");
                }
            }
        }
    }

    private void valiateProjectOpenStatus(ProjectVo projectVo) {
        if (ProjectOpenEnum.UNOPEN.getOpenStatus().equals(projectVo.getIsOpen())) {
            // 必须有证件号码采集项
            List<ProjectCollectInfoVo> collectInfoList = projectVo.getCollectInfoList();
            boolean falg = false;
            for (ProjectCollectInfoVo collectInfoVo : collectInfoList) {
                if ("idcard".equals(collectInfoVo.getComponentName())) {
                    falg = true;
                    break;
                }
            }
            if (!falg) {
                // 您还未添加证件号码，非开放性报名必须采集考生证件号码，请先添加
                throw new InvalidRequestException(Constant.RESPONSE_CODE_31010142);
            }
        }
    }

    /**
     * @Params:
     * @Description: 保存项目信息
     * @Author: STARF
     * @CreateDate: 2022/3/4 15:49
     **/
    private void saveProject(Project project) {
        if (StringUtils.isEmpty(project.getId())) {
            project.setId(UUIDUtils.newSortUUID());
            project.setCreateBy(iBaseLoginService.getUserId());
        }
        if (StringUtils.isEmpty(project.getAddress())) {
            project.setAddress(configManager.getAddress() + "?projectId=" + project.getId() + "&officeId=" + project.getOfficeId());
        }
        if (StringUtils.isEmpty(project.getQrCodeUrl())) {
            Map<String, Object> map = new HashMap<String, Object>();
            try {
                String scene = "offId=";
                if (project.getOfficeId().length() > 24) {
                    scene = scene + project.getOfficeId().substring(0, 24);
                } else {
                    scene = scene + project.getOfficeId();
                }
                map = (Map<String, Object>) tpsClient.getBmQRCodeHwCloud(project.getOfficeId(), scene, "pages/home/index", 260);
                log.info("getBmQRCodeHwCloud result :{}", JSON.toJSON(map));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
            if (null != map) {
                project.setQrCodeUrl(String.valueOf(map.get("obj")));
            }
        }
        if (null == project.getPayBeginTime()) {
            project.setPayBeginTime(project.getBeginTime());
        }
        if (null == project.getPayEndTime()) {
            project.setPayEndTime(project.getEndTime());
        }
        this.saveOrUpdate(project);
    }

    /**
     * @Params:
     * @Description: 保存项目科目
     * @Author: STARF
     * @CreateDate: 2022/3/4 15:56
     **/
    private void saveProSubject(ProjectVo projectVo) throws InvalidRequestException {
        List<ProjectSubject> list = projectSubjectService.querySubjectList(projectVo.getId(), null);
        List<ProjectSubjectVo> voList = projectVo.getSubjectList();

        if (CollUtil.isNotEmpty(voList)) {
            HashMap<String, Integer> map = new HashMap<String, Integer>();

            //list - projectVo.getSubjectList()取交集，交集删除
            if (CollUtil.isNotEmpty(list)) {
                List<ProjectSubject> resultList = list.stream().filter(item -> !voList.stream().map(ProjectSubject::getSubjectId).collect(Collectors.toList()).contains(item.getSubjectId()))
                        .collect(Collectors.toList());
                resultList.forEach(e -> {
                    try {
                        List<StudentSubject> ssList = iStudentSubjectServiceService.queryStudentSubject(null, null, e.getSubjectId(), null, new ArrayList<String>(), projectVo.getId());
                        if (CollUtil.isNotEmpty(ssList)) {
                            throw new InvalidRequestException(Constant.RESPONSE_CODE_31061501);
                        }
                        projectSubjectService.deleteSubject(e.getId(), e.getProjectId(), e.getSubjectId());
                    } catch (Exception exc) {
                        throw new InvalidRequestException(Constant.RESPONSE_CODE_31061502);
                    }
                });
            }

            //科目名称判重
            voList.forEach(s -> map.merge(s.getSubjectName(), 1, Integer::sum));
            for (Map.Entry<String, Integer> me : map.entrySet()) {
                if (me.getValue() > 1) {
                    throw new InvalidRequestException(Constant.RESPONSE_CODE_31010144);
                }
            }

            for (ProjectSubject subject : voList) {
                if (StringUtils.isEmpty(subject.getProjectId())) {
                    subject.setProjectId(projectVo.getId());
                }
                if (null == subject.getDelFlag()) {
                    subject.setDelFlag(false);
                }
                if (subject.getPrice() != null && subject.getPrice().doubleValue() > ConfigManager.MAXPRICE) {
                    throw new InvalidRequestException(Constant.RESPONSE_CODE_31010145);
                }
                if (0 == projectVo.getIsPay() && null != subject.getIsPay() && 1 == subject.getIsPay()) {
                    throw new InvalidRequestException(Constant.RESPONSE_CODE_31010108);
                }
                if (null != subject.getIsPay() && 1 == subject.getIsPay() && (null == subject.getPrice() || BigDecimal.ZERO.equals(subject.getPrice()))) {
                    throw new InvalidRequestException(Constant.RESPONSE_CODE_31051109);
                }
                if (StringUtils.isEmpty(subject.getSubjectName())) {
                    throw new InvalidRequestException(Constant.RESPONSE_CODE_31051109);
                }
                if (StringUtils.isEmpty(subject.getSubjectId())) {
                    subject.setSubjectId(UUIDUtils.newSortUUID());
                } else {
                    //已有考生的报名的科目，不能更新编辑
                    list.forEach(sub -> {
                        if (subject.getSubjectId().equals(sub.getSubjectId())) {
                            List<StudentSubject> ssList = iStudentSubjectServiceService.queryStudentSubject(null, null, sub.getSubjectId(), null, new ArrayList<String>(), projectVo.getId());
                            if (ssList != null && ssList.size() > 0 && hasChangeSubject(subject, sub)) {
                                throw new InvalidRequestException(Constant.RESPONSE_CODE_31061503);
                            }
                        }
                    });
                }
                if (StringUtils.isEmpty(subject.getId())) {
                    subject.setId(UUIDUtils.newSortUUID());
                }
            }
            if (CollUtil.isNotEmpty(voList)) {
                for (ProjectSubjectVo projectSubjectVo : voList) {
                    ProjectSubject projectSubject = new ProjectSubject();
                    BeanUtils.copyProperties(projectSubjectVo, projectSubject);
                    projectSubjectService.saveOrUpdate(projectSubject);
                }
            }
        } else {
            projectSubjectService.deleteSubjectByProjectId(projectVo.getId());
        }
    }

    /**
     * @Params:
     * @Description: 判断科目是否有修改
     * @Author: STARF
     * @CreateDate: 2022/5/26 16:03
     **/
    @Override
    public boolean hasChangeSubject(ProjectSubject subject1, ProjectSubject subject2) {
        if (!subject1.getSubjectId().equals(subject2.getSubjectId())
                || !subject1.getSubjectName().equals(subject2.getSubjectName())
                || (null != subject1.getPrice() && !subject1.getPrice().equals(subject2.getPrice()))
                || !subject1.getIsPay().equals(subject2.getIsPay())
                || !subject1.getDescription().equals(subject2.getDescription())) {
            return true;
        }
        return false;
    }

    @SneakyThrows
    @Override
    public Page<ProjectStatVo> queryPage(ProjectParam projectParam) {
        Page<ProjectStatVo> projectStatVoPage = projectMapper.queryProject(projectParam);
        List<ProjectStatVo> list = projectStatVoPage.getRecords();
        if (CollectionUtils.isNotEmpty(list)) {
            list.stream()
                    .parallel()
                    .forEach(statVo -> {
                        Set<String> deptIds = projectParam.getDeptIds();
                        // 如果没有机构采集信息则不设置机构权限查询
                        Boolean hasOfficePermission = iProjectCollectInfoService.isComponentRequired(statVo.getId(), ComponentNameEnum.office.getCode());
                        if (!hasOfficePermission) {
                            deptIds = null;
                        }

                        // 获取报名项目最新的创建时间
                        LocalDateTime latestCreateDate = projectStatisticService.getLatestCreateDate(statVo.getId());
                        projectStatisticService.getProjectStatistic(statVo.getId(), deptIds, latestCreateDate)
                                .ifPresent(projectStatistic -> {
                                    statVo.setAuditNum(projectStatistic.getPassAuditNum());
                                    statVo.setApplyNum(projectStatistic.getEnrolledNum());
                                });

                        if (statVo.getIsOpen() == 1) {
                            // 填充区域数据
                            this.fillUpPermissionData(statVo, latestCreateDate, hasOfficePermission, deptIds);
                        }
                    });
        }
        return projectStatVoPage;
    }

    @Override
    public ProjectVo queryProjectById(String id) {
        // 查询报名项目
        Project project = this.getById(id);
        ProjectVo projectVo = new ProjectVo();
        BeanUtils.copyProperties(project, projectVo);
        // 获取项目有所的科目信息
        List<ProjectSubjectVo> projectSubjectVos = new ArrayList<>();
        List<ProjectSubject> projectSubjects = projectSubjectService.querySubjectList(id, null);
        if (CollUtil.isNotEmpty(projectSubjects)) {
            for (ProjectSubject projectSubject : projectSubjects) {
                ProjectSubjectVo projectSubjectVo = new ProjectSubjectVo();
                BeanUtils.copyProperties(projectSubject, projectSubjectVo);
                projectSubjectVo.setCurrentRegSubjectCount(projectSubjectService.getCurrentRegSubjectCount(id, projectSubject.getSubjectId()));
                projectSubjectVos.add(projectSubjectVo);
            }
        }
        projectVo.setSubjectList(projectSubjectVos);
        projectVo.setNodeList(this.queryNodeList(id));
        try {
            projectVo.setCollectInfoList(iProjectCollectInfoService.queryByProjectId(id));
        } catch (Exception e) {
            log.error("查询报名项目采集信息异常", e);
        }
        return projectVo;
    }

    @Override
    public ProjectStatVo statProjectStudent(String projectId) {
        ProjectStatVo resultVo = new ProjectStatVo();
        Set<String> deptIds = iBaseLoginService.getDeptIds();
        Project project = this.getById(projectId);
        BeanUtils.copyProperties(project, resultVo);
        // 获取部门采集字段
        Boolean hasOfficePermission = iProjectCollectInfoService.isComponentRequired(projectId, ComponentNameEnum.office.getCode());
        if (!hasOfficePermission) {
            deptIds = null;
        }

        LocalDateTime latestCreateDate = projectStatisticService.getLatestCreateDate(projectId);

        ProjectStatistic projectStatistic = new ProjectStatistic();
        // 是否有区域参数收集
        if (hasOfficePermission) {
            // 区域报名数默认0
            resultVo.setApplyNum(CommonConstant.ZERO);
            // 如果没有机构采集字段，则为空
            Optional<ProjectStatistic> hasOfficeStatisticOptional = projectStatisticService.getProjectStatistic(projectId, deptIds, latestCreateDate);
            if (hasOfficeStatisticOptional.isPresent()) {
                projectStatistic = hasOfficeStatisticOptional.get();
            }
        } else {
            // 如果没有机构采集字段，则为空
            Optional<ProjectStatistic> nonOfficeStatisticOptional = projectStatisticService.getProjectStatistic(projectId, null, latestCreateDate);
            if (nonOfficeStatisticOptional.isPresent()) {
                projectStatistic = nonOfficeStatisticOptional.get();
            }
        }
        resultVo.setApplyNum(projectStatistic.getEnrolledNum());
        resultVo.setApplyManTime(projectStatistic.getPersonTimeNum());
        resultVo.setPayManTime(projectStatistic.getPaidNum());
        resultVo.setAuditNum(projectStatistic.getPendingNum());
        int num = iNonOpenService.statQualifications(projectId, deptIds);
        resultVo.setPermissionNum(num);
        return resultVo;
    }

    @Override
    public List<ProjectStatVo> getMiniList(String officeId, String regId) {
        List<ProjectStatVo> list = new ArrayList<>();
        if (StringUtils.isNotEmpty(regId)) {
            list = this.queryStudentProjecting(officeId, regId);
            if (CollectionUtils.isNotEmpty(list)) {
                for (ProjectStatVo studentProjectVo : list) {
                    //报考科目
                    int stuSujectNum = 0;
                    StudentProjectInfoVo studentProjectInfoVo = iStudentInfoService.getProjectSubjectById(studentProjectVo.getId(), regId);
                    if (CollUtil.isEmpty(studentProjectInfoVo.getStudentSubjectList())) {
                        studentProjectVo.setStuStatus(StuStatusEnum.COMMIT.getCode());
                        studentProjectVo.setStudentProjectPayStatusDesc("未报名");
                    } else {
                        StringBuilder stuSubjects = new StringBuilder();
                        for (ProjectSubjectVo projectSubjectVo : studentProjectInfoVo.getStudentSubjectList()) {
                            if (projectSubjectVo.getStatus().equals("1")) {
                                stuSubjects.append(projectSubjectVo.getSubjectName()).append(",");
                                stuSujectNum++;
                            }
                        }
                        if (stuSubjects.length() > 0) {
                            stuSubjects = new StringBuilder(stuSubjects.substring(0, stuSubjects.length() - 1));
                        }
                        studentProjectVo.setStudentSubjects(stuSubjects.toString());
                    }
                    if (stuSujectNum == 0) {
                        studentProjectVo.setStuStatus(StuStatusEnum.COMMIT.getCode());
                        studentProjectVo.setStudentProjectPayStatusDesc("未报名");
                        continue;
                    }
                    StudentProject studentProject = studentProjectMapper.getProjectStudent(studentProjectVo.getId(), regId);

                    //是否需要审核
                    if (studentProjectVo.getIsVerify() == 0) {
                        // 首先看下项目是不是需要缴费
                        if (studentProjectVo.getIsPay().equals(1)) {
                            // 查询考生是否有缴费科目
                            Map<String, Object> map = iStudentPayService.queryPayInfo2(studentProjectVo.getStudentId());
                            String code = (String) map.get("code");
                            studentProjectVo.setStudentProjectPayStatus(code);
                            studentProjectVo.setStudentProjectPayStatusDesc("待缴费");
                            studentProjectVo.setStuStatus(StuStatusEnum.UNCHECK_PAY_COMMIT.getCode());
                            if ("15038".equals(code)) {
                                studentProjectVo.setStudentProjectPayStatusDesc("待缴费");
                                studentProjectVo.setStuStatus(StuStatusEnum.UNCHECK_PAY_COMMIT.getCode());
                            }
                            if ("15046".equals(code)) {
                                studentProjectVo.setStudentProjectPayStatusDesc("无需缴费");
                                if (stuSujectNum == studentProjectVo.getMaxSubject()) {
                                    studentProjectVo.setStuStatus(StuStatusEnum.UNCHECK_PAY_PAID.getCode());
                                } else {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CONTINUE_BM.getCode());
                                }
                            }
                            if ("15044".equals(code)) {
                                // 考生暂无需缴费科目，请勿重复缴费！
                                studentProjectVo.setStudentProjectPayStatusDesc("已报考");
                                if (stuSujectNum == studentProjectVo.getMaxSubject()) {
                                    studentProjectVo.setStuStatus(StuStatusEnum.UNCHECK_PAY_PAID.getCode());
                                } else {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CONTINUE_BM.getCode());
                                }
                            }
                            if ("15048".equals(code)) {
                                // 您报名的科目暂未通过审核，请等待通过审核再进行缴费！
                                studentProjectVo.setStudentProjectPayStatusDesc("报名审核中");
                                studentProjectVo.setStuStatus(StuStatusEnum.CHECK_PAY_COMMIT.getCode());
                            }
                        } else {
                            studentProjectVo.setStudentProjectPayStatusDesc("无需缴费");
                            if (studentProjectInfoVo.getStudentSubjectList() != null && studentProjectInfoVo.getStudentSubjectList().size() > 0) {
                                studentProjectVo.setStuStatus(StuStatusEnum.UNCHECK_UNPAY_COMMIT.getCode());
                            }
                            if (stuSujectNum == studentProjectVo.getMaxSubject()) {
                                studentProjectVo.setStuStatus(StuStatusEnum.UNCHECK_UNPAY_COMMIT.getCode());
                            } else {
                                studentProjectVo.setStuStatus(StuStatusEnum.CONTINUE_BM.getCode());
                            }
                        }
                    } else {
                        // 首先看下项目是不是需要缴费
                        if (studentProjectVo.getIsPay().equals(1)) {
                            // 查询考生是否有缴费科目
                            Map<String, Object> map = iStudentPayService.queryPayInfo2(studentProjectVo.getStudentId());
                            String code = (String) map.get("code");
                            studentProjectVo.setStudentProjectPayStatus(code);
                            studentProjectVo.setStuStatus(StuStatusEnum.CHECK_PAY_CHECKED.getCode());
                            studentProjectVo.setStudentProjectPayStatusDesc("待缴费");
                            if ("15038".equals(code)) {
                                studentProjectVo.setStudentProjectPayStatusDesc("待缴费");
                            }
                            if ("15046".equals(code)) {
                                studentProjectVo.setStudentProjectPayStatusDesc("无需缴费");
                                if (studentProjectVo.getAuditState() == 0) {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CHECK_UNPAY_COMMIT.getCode());
                                } else if (studentProjectVo.getAuditState() == 1) {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CHECK_UNPAY_CHECKED.getCode());
                                } else {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CHECK_UNPAY_UNCHECKED.getCode());
                                }
                            }
                            if ("15044".equals(code)) {
                                // 考生暂无需缴费科目，请勿重复缴费！
                                studentProjectVo.setStudentProjectPayStatusDesc("已报考");
                                if (stuSujectNum == studentProjectVo.getMaxSubject()) {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CHECK_PAY_PAID.getCode());
                                } else {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CONTINUE_BM.getCode());
                                }
                            }
                            if ("15048".equals(code)) {
                                // 您报名的科目暂未通过审核，请等待通过审核再进行缴费！
                                studentProjectVo.setStudentProjectPayStatusDesc("报名审核中");
                                studentProjectVo.setStuStatus(StuStatusEnum.CHECK_PAY_COMMIT.getCode());
                            }

                            if (studentProject.getIsAudit() == 2) {
                                studentProjectVo.setStuStatus(StuStatusEnum.CHECK_PAY_UNCHECKED.getCode());
                            }
                        } else {
                            studentProjectVo.setStudentProjectPayStatusDesc("无需缴费");
                            studentProjectVo.setStuStatus(StuStatusEnum.CHECK_UNPAY_COMMIT.getCode());
                            if (studentProject.getIsAudit() == 1) {
                                if (stuSujectNum == studentProjectVo.getMaxSubject()) {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CHECK_UNPAY_CHECKED.getCode());
                                } else {
                                    studentProjectVo.setStuStatus(StuStatusEnum.CONTINUE_BM.getCode());
                                }
                            }
                            if (studentProject.getIsAudit() == 2) {
                                studentProjectVo.setStuStatus(StuStatusEnum.CHECK_UNPAY_UNCHECKED.getCode());
                            }
                        }
                    }
                }
            }
        }
        String now = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        QueryWrapper<Project> wrapper = new QueryWrapper<Project>();
        if (StringUtils.isNotEmpty(officeId)) {
            wrapper.eq("office_id", officeId);
        }
        wrapper.eq("is_del_flag", 0);
        wrapper.eq("status", 1);
        wrapper.apply("UNIX_TIMESTAMP(end_time) >= UNIX_TIMESTAMP('" + now + "')");
        List<Project> list2 = this.list(wrapper);

        list2.sort((p1, p2) -> p2.getCreateDate().compareTo(p1.getCreateDate()));
        List<StudentProject> studentProjectList = new ArrayList<>();
        for (Project project : list2) {
            ProjectStatVo vo = new ProjectStatVo();
            BeanUtils.copyProperties(project, vo);
            StudentProject studentProject = studentProjectMapper.getProjectStudent(project.getId(), regId);
            if (null == vo.getStuStatus()) {
                if (null != studentProject) {
                    vo.setStuStatus(1);
                }
            }
            if (null != studentProject) {
                studentProjectList.add(studentProject);
            }
        }

        studentProjectList.sort((p1, p2) -> p2.getUpdateDate().compareTo(p1.getUpdateDate()));
        List<ProjectStatVo> voList = new ArrayList<>();
        for (StudentProject studentProject : studentProjectList) {
            boolean flag = true;
            for (ProjectStatVo vo : list) {
                if (vo.getId().equals(studentProject.getProjectId())) {
                    voList.add(vo);
                    flag = false;
                    break;
                }
            }
            if (flag) {
                for (Project project : list2) {
                    if (project.getId().equals(studentProject.getProjectId())) {
                        ProjectStatVo vo = new ProjectStatVo();
                        BeanUtils.copyProperties(project, vo);
                        voList.add(vo);
                        break;
                    }
                }
            }
        }

        for (StudentProject studentProject : studentProjectList) {
            for (Project project : list2) {
                if (!project.getId().equals(studentProject.getProjectId())) {
                    ProjectStatVo vo = new ProjectStatVo();
                    BeanUtils.copyProperties(project, vo);
                    voList.add(vo);
                }
            }
        }
        for (Project project : list2) {
            boolean flag = false;
            for (ProjectStatVo vo : voList) {
                if (project.getId().equals(vo.getId())) {
                    flag = true;
                }
            }
            if (!flag) {
                ProjectStatVo vo = new ProjectStatVo();
                BeanUtils.copyProperties(project, vo);
                voList.add(vo);
            }
        }
        Set<String> set = new HashSet<String>();
        Iterator<ProjectStatVo> iterator = voList.listIterator();
        while (iterator.hasNext()) {
            ProjectStatVo vo = iterator.next();
            if (set.contains(vo.getId())) {
                iterator.remove();
            } else {
                set.add(vo.getId());
            }
        }
        return voList;
    }

    @Override
    public String uploadFile(MultipartFile file) {
        if (null == file) {
            BusinessLogicException.throwException(Constant.RESPONSE_CODE_31051903, "文件为空");
        }
        String fileName = file.getOriginalFilename();
        String fileType = KqStringUtils.getFilenameExtension(fileName);
        if ("png".equalsIgnoreCase(fileType) || "jpg".equalsIgnoreCase(fileType)) {
            long size = file.getSize();
            double fileSize = (double) size / (1024.00 * 1024);
            if (fileSize > 10.00) {
                // 单位m
                BusinessLogicException.throwException(Constant.RESPONSE_CODE_31051901, "图片过大，请重新上传");
            }
            String fileSysPath = fileClient.uploadImageAndCrtThumbImage(file);
            return fileSysPath;
        } else {
            throw new BusinessLogicException(Constant.RESPONSE_CODE_31051902, "图片格式不正确");
        }
    }

    @SneakyThrows
    @Override
    public List<ProjectStatVo> queryEffectiveProject(String officeId, String regId) {
        List<ProjectStatVo> list = this.queryStudentProjecting(officeId, null);
        List<ProjectStatVo> stuList = this.queryStudentProjectIgnoreSubject(officeId, regId);
        List<ProjectStatVo> newList = new ArrayList<>();
        for (ProjectStatVo vo : list) {
            boolean flag = false;
            for (ProjectStatVo statVo : stuList) {
                if (vo.getId().equals(statVo.getId())) {
                    flag = true;
                }
            }
            if (!flag) {
                newList.add(vo);
            }
        }
        return newList;
    }

    @SneakyThrows
    @Override
    public ApiResponse<Project> publishProject(Project project) {
        if (project.getBeginTime() != null && project.getBeginTime().isAfter(project.getEndTime())) {
            return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051101).build();
        }

        // 校验项目的名称是否重复
        ApiResponse<Project> validateProjectNameRepitResult = validateProjectNameRepit(project);
        if (null != validateProjectNameRepitResult) {
            return validateProjectNameRepitResult;
        }

        // 校验项目的支付时间
        ApiResponse<Project> payTimeValidateResult = validatePayTimes(project);
        if (null != payTimeValidateResult) {
            return payTimeValidateResult;
        }

        // 校验项目的退费时间
        ApiResponse<Project> valiateProjectRefuseTimesResult = valiateProjectRefuseTimes(project);
        if (null != valiateProjectRefuseTimesResult) {
            return valiateProjectRefuseTimesResult;
        }

        // 如果报名是发布
        if (NumberUtil.equals(project.getStatus(), CommonConstant.ONE)) {
            List<ProjectNodeVo> projectNodeVos = iProjectNodeService.queryNodeList(project.getId());
            if (CollUtil.isEmpty(projectNodeVos)) {
                BusinessLogicException.throwException(31051117, "报名节点未添加不能进行发布");
            }

            List<ProjectCollectInfoVo> projectCollectInfoVos = iProjectCollectInfoService.queryByProjectId(project.getId());
            if (CollUtil.isEmpty(projectCollectInfoVos)) {
                BusinessLogicException.throwException(31051116, "考生采集信息未添加不能进行发布");
            }

            List<ProjectSubject> projectSubjectsByProjectId = projectSubjectService.getProjectSubjectsByProjectId(project.getId());
            if (CollUtil.isEmpty(projectSubjectsByProjectId)) {
                BusinessLogicException.throwException(31051115, "科目未添加不能进行发布");
            }
        }

        Project oldProject = this.getById(project.getId());
        // 兼容因为其他原因导致二维码出不来，需要重新生成下
        if (StringUtils.isEmpty(oldProject.getQrCodeUrl())) {
            String qrcodeUrl = getBmProjectQrCodeUrl(oldProject.getOfficeId());
            log.debug("[发布取消发布报名]小程序生产二维码：{}", qrcodeUrl);
            if (StrUtil.isNotBlank(qrcodeUrl)) {
                project.setQrCodeUrl(qrcodeUrl);
            }
        }
        project.setPtBangFlag(oldProject.getPtBangFlag());
        boolean bool = this.saveOrUpdate(project);
        if (bool) {
            if (project.getStatus() != null && project.getStatus().intValue() == 1) {
                User user = iBaseLoginService.getSysUser();
                //sendSysMessage2Pt(project.getId(),user.getName(),user.getId());
            }
            return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_SUCCESS).data(project).build();
        } else {
            return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31010101).data(project).build();
        }
    }

    @SuppressWarnings("unchecked")
    private String getBmProjectQrCodeUrl(String officeId) {
        Map<String, Object> map = new HashMap<String, Object>();
        String qrCodeUr = null;
        if (StrUtil.isEmpty(officeId)) {
            return qrCodeUr;
        }
        try {
            String scene = "offId=";
            if (officeId.length() > 24) {
                scene = scene + officeId.substring(0, 24);
            } else {
                scene = scene + officeId;
            }
            map = (Map<String, Object>) tpsClient.getBmQRCodeHwCloud(officeId, scene, "pages/home/index", 260);
            log.info("[发布取消发布报名]微信小程序二维码生成 scene:{},result:{}", scene, JSON.toJSON(map));
        } catch (Exception e) {
            log.error("[发布取消发布报名]获取微信小程序二维码异常", e);
        }
        if (null != map) {
            qrCodeUr = String.valueOf(map.get("obj"));
        }
        return qrCodeUr;
    }

    /***
     * 校验项目的退费时间
     * @param project
     * @return
     */
    private ApiResponse<Project> valiateProjectRefuseTimes(Project project) {
        if (null != project.getRefuseTime() && (project.getRefuseTime().isBefore(project.getBeginTime()) || project.getRefuseTime().isAfter(project.getEndTime()))) {
            return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051105).data(project).build();
        }
        if (null != project.getPayBeginTime() && (project.getPayBeginTime().isBefore(project.getBeginTime()) || project.getPayBeginTime().isAfter(project.getEndTime()))) {
            return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051106).data(project).build();
        }
        if (null != project.getPayEndTime() && (project.getPayEndTime().isBefore(project.getBeginTime()) || project.getPayEndTime().isAfter(project.getEndTime()))) {
            return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051106).data(project).build();
        }
        return null;
    }

    /***
     * 校验同机构下面项目名称是否重复
     * @param project
     * @return
     */
    private ApiResponse<Project> validateProjectNameRepit(Project project) {
        QueryWrapper<Project> tempwrapper = new QueryWrapper<Project>();
        tempwrapper.eq("office_id", project.getOfficeId());
        tempwrapper.eq("project_name", project.getProjectName());
        List<Project> projectList = this.list(tempwrapper);
        if (CollectionUtils.isNotEmpty(projectList)) {
            boolean flag = false;
            for (int i = 0; i < projectList.size(); i++) {
                Project tempProject = projectList.get(i);
                if (tempProject.getId().equals(project.getId()) && tempProject.getProjectName().equals(project.getProjectName())) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051107).data(project).build();
            }
        }
        return null;
    }

    /**
     * 校验支付时间
     *
     * @param project
     * @return
     */
    private ApiResponse<Project> validatePayTimes(Project project) {
        if (project.getPayBeginTime() != null) {
            if (project.getPayBeginTime().isAfter(project.getPayEndTime())) {
                return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051108).build();
            }
            if (project.getPayEndTime().isBefore(project.getBeginTime())) {
                return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051108).build();
            }
            if (project.getPayEndTime().isBefore(project.getEndTime())) {
                return ApiResponse.<Project>builder().code(Constant.RESPONSE_CODE_31051108).build();
            }
        }
        return null;
    }

    /**
     * 填充区域数据
     *
     * @param statVo
     * @param latestCreateDate
     * @param deptIds
     */
    private void fillUpPermissionData(ProjectStatVo statVo, LocalDateTime latestCreateDate, Boolean hasOfficePermission, Set<String> deptIds) {
        // 是否有区域参数收集
        if (hasOfficePermission) {
            // 区域报名数默认0
            statVo.setApplyNum(CommonConstant.ZERO);
            // 有配置区域权限
            projectStatisticService.getProjectStatistic(statVo.getId(), deptIds, latestCreateDate)
                    .map(ProjectStatistic::getEnrolledNum)
                    .ifPresent(statVo::setApplyNum);
        }
    }

    /**
     * @Params:
     * @Description: 保存项目流程
     * @Author: STARF
     * @CreateDate: 2022/3/4 16:25
     **/
    private void saveProjectNode(List<ProjectNodeVo> nodeVoList, Project project) {
        int sort = 1;
        List<ProjectNode> list = new ArrayList<>();
        // 查询是否有存在节点配置信息
        List<ProjectNodeVo> oldList = queryNodeList(project.getId());
        if (oldList.isEmpty()) {
            //报名须知
            ProjectNode notice = ProjectNode.builder().build();
            BeanUtils.copyProperties(nodeVoList.get(0), notice);
            notice.setId(UUIDUtils.newSortUUID());
            notice.setProjectId(project.getId());
            notice.setNodeNumber((sort++) + "");
            notice.setNodeType(NodeTypeEnum.NOTICE.getCode());
            notice.setDelFlag(false);
            list.add(notice);

            //基本信息
            ProjectNode baseInfo = ProjectNode.builder().projectId(project.getId()).nodeOrder("1").nodeType(NodeTypeEnum.BASEINFO.getCode()).nodeName(NodeTypeEnum.BASEINFO.getMsg()).nodeNumber((sort++) + "").build();
            baseInfo.setId(UUIDUtils.newSortUUID());
            baseInfo.setDelFlag(false);
            list.add(baseInfo);

            //报名科目
            ProjectNode subject = ProjectNode.builder().projectId(project.getId()).nodeOrder("1").nodeType(NodeTypeEnum.SUBJECT.getCode()).nodeName(NodeTypeEnum.SUBJECT.getMsg()).nodeNumber((sort++) + "").build();
            subject.setId(UUIDUtils.newSortUUID());
            subject.setDelFlag(false);
            list.add(subject);

            //资格审核
            ProjectNode verity = ProjectNode.builder().projectId(project.getId()).nodeOrder("1").nodeType(NodeTypeEnum.VERIFY.getCode()).nodeName(NodeTypeEnum.VERIFY.getMsg()).nodeNumber((sort++) + "").build();
            verity.setId(UUIDUtils.newSortUUID());
            verity.setDelFlag(false);
            list.add(verity);

            //缴费
            ProjectNode pay = ProjectNode.builder().projectId(project.getId()).nodeOrder("1").nodeType(NodeTypeEnum.PAY.getCode()).nodeName(NodeTypeEnum.PAY.getMsg()).nodeNumber((sort++) + "").build();
            pay.setId(UUIDUtils.newSortUUID());
            pay.setDelFlag(false);
            list.add(pay);

            //打印准考证
            ProjectNode admission = ProjectNode.builder().projectId(project.getId()).nodeOrder("1").nodeType(NodeTypeEnum.ADMISSION.getCode()).nodeName(NodeTypeEnum.ADMISSION.getMsg()).nodeNumber((sort++) + "").build();
            admission.setId(UUIDUtils.newSortUUID());
            admission.setDelFlag(false);
            list.add(admission);

            iProjectNodeService.saveOrUpdateBatch(list);
        } else {
            if (CollectionUtils.isNotEmpty(nodeVoList)) {
                for (ProjectNodeVo projectNodeVo : nodeVoList) {
                    if (StringUtils.isNotEmpty(projectNodeVo.getId())) {
                        ProjectNode notice = ProjectNode.builder().build();
                        BeanUtils.copyProperties(projectNodeVo, notice);
                        if (StringUtils.isNotEmpty(notice.getNodeType()) && "1".equals(notice.getNodeType())) {
                            notice.setNodeType(NodeTypeEnum.NOTICE.getCode());
                        }
                        notice.setDelFlag(false);
                        notice.setProjectId(project.getId());
                        iProjectNodeService.updateById(notice);
                    }
                }
            }
        }
    }

    @Override
    public ProjectPreviewVo previewProject(String projectId) throws Exception {
        if (StringUtils.isNotEmpty(projectId)) {
            throw new Exception("项目id不能为空");
        }
        ProjectPreviewVo vo = new ProjectPreviewVo();
        try {
            vo = (ProjectPreviewVo) this.getById(projectId);
            QueryWrapper<ProjectSubject> wrapper = new QueryWrapper();
            wrapper.eq("project_id", projectId);
            List<ProjectSubject> projectSubjectList = projectSubjectService.list(wrapper);
            QueryWrapper<ProjectCollectInfo> optionWrapper = new QueryWrapper();
            optionWrapper.eq("project_id", projectId);
            List<ProjectCollectInfo> projectCollectInfoList = projectCollectInfoService.list(optionWrapper);
            if (vo != null) {
                vo.setProjectSubjectList(projectSubjectList);
                vo.setProjectCollectInfoList(projectCollectInfoList);
            }

        } catch (Exception ex) {
            throw new Exception("预览报名项目失败");
        }

        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProject(String projecId) throws Exception {
        if (StringUtils.isEmpty(projecId)) {
            throw new Exception("项目id不能为空");
        }
        try {
            this.removeById(projecId);
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("project_id", projecId);
            projectCollectInfoService.removeByMap(map);
            iProjectCollectInfoOptionService.removeByMap(map);
            iProjectImageStandardService.removeByMap(map);
            iStudentProjectServiceService.removeByMap(map);
            iStudentSubjectServiceService.removeByMap(map);
            iStudentInfoServiceService.removeByMap(map);
        } catch (Exception ex) {
            throw new Exception("删除项目失败");
        }
    }

    @Override
    public ProjectStatVo statProjectStudent(String projectId, List<String> permissionList, String deptId) {
        return projectMapper.statProjectStudent(projectId, permissionList, deptId);
    }

    @Override
    public ProjectStatVo statProjectSubject(String projectId, List<String> permissionList, String deptId) {
        return projectMapper.statProjectSubject(projectId, permissionList, deptId);
    }

    @Override
    public List<ProjectStatVo> queryStudentProject(String officeId, String regId) throws Exception {
        List<ProjectStatVo> list = new ArrayList<ProjectStatVo>();
        try {
            list = projectMapper.queryStudentProject(officeId, regId);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw ex;
        }
        return list;
    }

    @Override
    public List<ProjectStatVo> queryStudentProjectIgnoreSubject(String officeId, String regId) throws Exception {
        List<ProjectStatVo> list = new ArrayList<ProjectStatVo>();
        try {
            list = projectMapper.queryStudentProjectIgnoreSubject(officeId, regId);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw ex;
        }
        return list;
    }

    @Override
    public List<ProjectStatVo> queryStudentProjecting(String officeId, String regId) {
        List<ProjectStatVo> list = projectMapper.queryStudentProjecting(officeId, regId, LocalDateTime.now());
        return list;
    }

    @Override
    public List<ProjectStatVo> queryStudentProjected(String officeId, String regId) {
        List<ProjectStatVo> list = projectMapper.queryStudentProjected(officeId, regId, LocalDateTime.now());
        if (CollectionUtils.isNotEmpty(list)) {
            list.stream()
                    .parallel()
                    .forEach(studentProjectVo -> {
                        // 首先看下项目是不是需要缴费
                        if (studentProjectVo.getIsPay().equals(1)) {
                            // 查询考生是否有缴费科目
                            Map<String, Object> map = iStudentPayService.queryPayInfo2(studentProjectVo.getStudentId());
                            String code = (String) map.get("code");
                            studentProjectVo.setStudentProjectPayStatus(code);
                            studentProjectVo.setStudentProjectPayStatusDesc("待缴费");
                            if ("15038".equals(code)) {
                                studentProjectVo.setStudentProjectPayStatusDesc("待缴费");
                            }
                            if ("15046".equals(code)) {
                                studentProjectVo.setStudentProjectPayStatusDesc("无需缴费");
                            }
                            if ("15044".equals(code)) {
                                // 考生暂无需缴费科目，请勿重复缴费！
                                studentProjectVo.setStudentProjectPayStatusDesc("已报考");
                            }
                            if ("15048".equals(code)) {
                                // 您报名的科目暂未通过审核，请等待通过审核再进行缴费！
                                studentProjectVo.setStudentProjectPayStatusDesc("报名审核中");
                            }
                        } else {
                            studentProjectVo.setStudentProjectPayStatusDesc("无需缴费");
                        }
                        try {
                            //报考科目
                            int stuSujectNum = 0;
                            StudentProjectInfoVo studentProjectInfoVo = iStudentInfoService.getProjectSubjectById(studentProjectVo.getId(), regId);
                            if (studentProjectInfoVo.getStudentSubjectList() == null || studentProjectInfoVo.getStudentSubjectList().size() == 0) {
                                studentProjectVo.setStuStatus(StuStatusEnum.COMMIT.getCode());
                                studentProjectVo.setStudentProjectPayStatusDesc("未报名");
                            } else {
                                StringBuilder stuSubjects = new StringBuilder();
                                for (ProjectSubjectVo projectSubjectVo : studentProjectInfoVo.getStudentSubjectList()) {
                                    if (projectSubjectVo.getStatus().equals("1")) {
                                        stuSubjects.append(projectSubjectVo.getSubjectName()).append(",");
                                        stuSujectNum++;
                                    }
                                }
                                stuSubjects = new StringBuilder(stuSubjects.substring(0, stuSubjects.length() - 1));
                                studentProjectVo.setStudentSubjects(stuSubjects.toString());
                            }
                        } catch (Exception e) {
                            log.error("miniRecord----projectId={}, regId={}, e={}", studentProjectVo.getId(), regId, e);
                        }
                    });
        }
        return list;
    }

    @Override
    public FirstStatVo statStudent(ProjectParam param) throws Exception {
        Integer count = 0;
        FirstStatVo vo = new FirstStatVo();
        count = iUserPermissionService.statStudent(param.getUserId(), param.getDeptIds(), null, null);
        vo.setBmTotalNum(count);
        if ("0".equals(param.getState())) {
            param.setStartTime(DateUtil.dateToStr(new Date()) + " 00:00:00");
            param.setEndTime(DateUtil.dateToStr(new Date()) + " 23:59:59");
        }
        if ("1".equals(param.getState())) {
            param.setStartTime(DateUtil.firstDayOfWeek(new Date()) + " 00:00:00");
            param.setEndTime(DateUtil.lastDayOfWeek() + " 23:59:59");
        }
        if ("2".equals(param.getState())) {
            param.setStartTime(DateUtil.getFirstDayDateOfMonth(new Date()) + " 00:00:00");
            param.setEndTime(DateUtil.getLastDayOfMonth(new Date()) + " 23:59:59");
        }
        count = iUserPermissionService.statStudent(param.getUserId(), param.getDeptIds(), param.getStartTime(), param.getEndTime());
        ApiResponse api = kwClient.countFinishCandidateNumber(param.getStartTime(), param.getEndTime(), param.getOfficeId());
        Map<String, Object> map = (Map<String, Object>) api.getData();
        vo.setKsAddNum(Integer.valueOf(map.get("addNum").toString()));
        vo.setKsTotalNum(Integer.valueOf(map.get("totalNum").toString()));
        api = tkClient.itemCountStatistics(param.getStartTime(), param.getEndTime());
        map = (Map<String, Object>) api.getData();
        vo.setStAddNum(Integer.valueOf(map.get("newly").toString()));
        vo.setStTotalNum(Integer.valueOf(map.get("total").toString()));
        api = tkClient.paperCountStatistics(param.getStartTime(), param.getEndTime());
        map = (Map<String, Object>) api.getData();
        vo.setKwAddNum(Integer.valueOf(map.get("newly").toString()));
        vo.setKwTotalNum(Integer.valueOf(map.get("total").toString()));
        vo.setBmAddNum(count);
        return vo;
    }

    @Override
    public List<ProjectNodeVo> queryNodeList(String projectId) {
        // 报名流程配置
        List<ProjectNodeVo> list = iProject2Service.queryProjectNode(projectId);
        return list;
    }

    @Override
    public void updateNodeList(List<ProjectNodeVo> list) {

        for (int i = 0; i < list.size(); i++) {
            nodeMapper.updateNodeList(list.get(i));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean copyProject(ProjectCopyVo projectCopyVo) throws Exception {
        Project project = this.getById(projectCopyVo.getOldProjectId());
        String oldProjectId = projectCopyVo.getOldProjectId();
        String newProjectId = UUIDUtils.newSortUUID();
        Project tempProject = new Project();
        BeanUtils.copyProperties(project, tempProject);
        tempProject.setId(newProjectId);
        tempProject.setAddress(configManager.getAddress() + "?projectId=" + newProjectId + "&officeId=" + project.getOfficeId());
        tempProject.setProjectName(projectCopyVo.getNewProjectName());
        tempProject.setCreateBy(iBaseLoginService.getUserId());
        tempProject.setCreateDate(LocalDateTime.now());
        tempProject.setUpdateBy(iBaseLoginService.getUserId());
        tempProject.setUpdateDate(LocalDateTime.now());
        tempProject.setDelFlag(Boolean.FALSE);
        // 默认为未发布状态
        tempProject.setStatus(BmProjectStateEnum.UNPUBLISHED.getState());
        
        
        Map<String, Object> map3 = new HashMap<String, Object>();
        try {
            String scene = "offId=";
            if (project.getOfficeId().length() > 24) {
                scene = scene + project.getOfficeId().substring(0, 24);
            } else {
                scene = scene + project.getOfficeId();
            }
            map3 = (Map<String, Object>) tpsClient.getBmQRCodeHwCloud(project.getOfficeId(), scene, "pages/home/index", 260);
            log.info("-------getBmQRCode-------" + map3.toString());
        } catch (Exception e) {
            log.error("# copyProject error", e);
            return Boolean.FALSE;
        }
        tempProject.setQrCodeUrl(map3.get("obj").toString());
        this.save(tempProject);

        List<ProjectSubject> projectSubjectList = projectSubjectService.querySubjectList(projectCopyVo.getOldProjectId(), null);
        List<ProjectSubject> tempSubjectList = new ArrayList<>();
        for (int i = 0; i < projectSubjectList.size(); i++) {
            ProjectSubject tempProjectSubject = new ProjectSubject();
            BeanUtils.copyProperties(projectSubjectList.get(i), tempProjectSubject);
            tempProjectSubject.setProjectId(newProjectId);
            tempProjectSubject.setSubjectId(UUIDUtils.newSortUUID());
            tempProjectSubject.setId(UUIDUtils.newSortUUID());
            tempSubjectList.add(tempProjectSubject);
        }
        projectSubjectService.saveBatch(tempSubjectList);
        
        List<ProjectCollectInfo> projectCollectInfoList = projectCollectInfoService.lambdaQuery()
                .eq(ProjectCollectInfo::getProjectId, oldProjectId)
                .eq(ProjectCollectInfo::getDelFlag, DelFlagEnum.NORMAL.getCode())
                .list();
        Map<String, String> map = new HashMap<>();
        if(CollUtil.isNotEmpty(projectCollectInfoList)) {
            for (ProjectCollectInfo projectCollectInfo : projectCollectInfoList) {
                String uuid = UUIDUtils.newSortUUID();
                ProjectCollectInfo tempProjectCollectInfo = new ProjectCollectInfo();
                BeanUtils.copyProperties(projectCollectInfo, tempProjectCollectInfo);
                tempProjectCollectInfo.setProjectId(newProjectId);
                tempProjectCollectInfo.setId(uuid);
                map.put(projectCollectInfo.getId(), uuid);
                projectCollectInfoService.save(tempProjectCollectInfo);
            }
        }

        List<ProjectCollectInfoOption> optionList = iProjectCollectInfoOptionService.queryByProjectId(projectCopyVo.getOldProjectId());
        if(CollUtil.isNotEmpty(optionList)) {
            for (ProjectCollectInfoOption projectCollectInfoOption : optionList) {
                ProjectCollectInfoOption tempProjectCollectInfoOption = new ProjectCollectInfoOption();
                BeanUtils.copyProperties(projectCollectInfoOption, tempProjectCollectInfoOption);
                tempProjectCollectInfoOption.setProjectId(newProjectId);
                tempProjectCollectInfoOption.setId(UUIDUtils.newSortUUID());
                tempProjectCollectInfoOption.setProjectCollectInfoId(map.get(projectCollectInfoOption.getProjectCollectInfoId()));
                iProjectCollectInfoOptionService.save(tempProjectCollectInfoOption);
            }
        }

        List<ProjectImageStandard> standardList = iProjectImageStandardService.queryByProjectId(projectCopyVo.getOldProjectId());
        if (CollUtil.isNotEmpty(standardList)) {
            for (ProjectImageStandard projectImageStandard : standardList) {
                ProjectImageStandard tempProjectImageStandard = new ProjectImageStandard();
                BeanUtils.copyProperties(projectImageStandard, tempProjectImageStandard);
                tempProjectImageStandard.setProjectId(newProjectId);
                tempProjectImageStandard.setId(UUIDUtils.newSortUUID());
                tempProjectImageStandard.setProjectCollectInfoId(map.get(projectImageStandard.getProjectCollectInfoId()));
                iProjectImageStandardService.save(tempProjectImageStandard);
            }
        }
        
        List<ProjectNodeVo> nodeList = this.queryNodeList(projectCopyVo.getOldProjectId());
        List<ProjectNode> tempList = new ArrayList<>();
        if (CollUtil.isNotEmpty(nodeList)) {
            for (ProjectNodeVo projectNodeVo : nodeList) {
                ProjectNode tempProjectNodeVo = new ProjectNode();
                BeanUtils.copyProperties(projectNodeVo, tempProjectNodeVo);
                tempProjectNodeVo.setId(UUIDUtils.newSortUUID());
                tempProjectNodeVo.setProjectId(newProjectId);
                tempList.add(tempProjectNodeVo);
            }
            iProjectNodeService.saveBatch(tempList);
        }
        return Boolean.TRUE;
    }

    @Override
    public List<Project> queryExamProject(String officeId, Set<String> deptIds) {
        return projectMapper.queryExamProject(officeId, deptIds);
    }

    @Override
    public String getRealOfficeId(String officeId) {
        List<Project> objList = projectMapper.getRealOfficeId(officeId);
        if (objList.size() == 0) {
            return null;
        }
        return objList.get(objList.size() - 1).getOfficeId();
    }

    @Override
    public Long pushExamNotify(List<PushExamNotifyDTO> pushExamNotifyDTOS) {
        Long pushNum = NumberEnum.ZERO.getK().longValue();

        // 洗刷推送考试通知
        PushExamNotifyDTO[] pushExamNotifyDTOArrays = this.washExamPushNotify(pushExamNotifyDTOS);

        // 如果推送数据不为空则推送
        if (ArrayUtil.isNotEmpty(pushExamNotifyDTOArrays)) {
            pushNum = redisTemplate.boundListOps(WeChatMiniNotifyPushCommand.WECHAT_MINI_NOTIFY_PUSH_QUEUE_KEY).rightPushAll(pushExamNotifyDTOArrays);
        }

        return pushNum;
    }

    /**
     * 洗刷推送考试通知并截取最大长度
     *
     * @param pushExamNotifyDTOS
     * @return
     */
    private PushExamNotifyDTO[] washExamPushNotify(List<PushExamNotifyDTO> pushExamNotifyDTOS) {
        Integer pushExamUserNotifyNum = configManager.getPushExamUserNotifyNum();

        List<PushExamNotifyDTO> pushExamNotifyDTOList = pushExamNotifyDTOS.stream()
                .parallel()
                .filter(data -> StrUtil.isNotBlank(data.getId()))
                .filter(data -> StrUtil.isNotBlank(data.getName()))
                .filter(data -> ObjectUtil.isNotNull(data.getStartTime()))
                .filter(data -> ObjectUtil.isNotNull(data.getEndTime()))
                .filter(data -> StrUtil.isNotBlank(data.getSubjectName()))
                .collect(Collectors.toList());

        // 获取差集日志打印
        Collection<PushExamNotifyDTO> subtractNotifyDTOS = CollUtil.disjunction(pushExamNotifyDTOS, pushExamNotifyDTOList);
        subtractNotifyDTOS.forEach(data -> log.error("# 22.2.1 考务推送考试安排通知洗刷问题数据：{}", JSON.toJSONString(data)));

        return pushExamNotifyDTOList.stream()
                .limit(pushExamUserNotifyNum)
                .toArray(PushExamNotifyDTO[]::new);
    }

    /**
     * @Params:
     * @Description: 批量修改报名项目采集信息
     * @Author: STARF
     * @CreateDate: 2022/3/8 15:46
     **/
    @Override
    public Object updateProjectCollect(List<ProjectCollectInfoVo> list) {
        int num = 0;
        int required = 0;
        for (int i = 0; i < list.size(); i++) {
            if ("certificatetype".equals(list.get(i).getComponentName())) {
                num = list.get(i).getOrderNum();
                required = list.get(i).getRequired();
            }
        }
        for (int i = 0; i < list.size(); i++) {
            if (num != 0 && "idcard".equals(list.get(i).getComponentName())) {
                list.get(i).setOrderNum(num);
                list.get(i).setRequired(required);
            }
        }
        return ApiResponse.builder().code(Constant.RESPONSE_CODE_SUCCESS).build();
    }

    /**
     * @Params:
     * @Description: 文件上传华为云
     * @Author: STARF
     * @CreateDate: 2022/3/9 16:33
     **/
    @Override
    public String uploadFileToHwCloud(MultipartFile file, String fileType) throws IOException {
        String suffixName = StringUtils.EMPTY;
        String path = hwcloudPath + (StringUtils.isEmpty(fileType) ? "bm" : fileType) + "/" + UUIDUtils.newSortUUID() + suffixName;
        byte[] bytes = file.getBytes();
        Result result = tpsClient.uploadFileToHuawei("", path, bytes);
        log.info(JSON.toJSONString(result));
        if (result.getSuccess()) {
            return result.getObj().toString().replace("%2F", "/");
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void statisticProject(String projectId) {
        LocalDateTime createDate = LocalDateTime.now();
        Project project = this.getById(projectId);
        if (ObjectUtil.isEmpty(project)) {
            log.debug("[报名统计]消费报名数据为查询到报名项目：{}", projectId);
            return;
        }

        String watchName = StrUtil.format("{}:{}", project.getProjectName(), project.getId());
        StopWatch stopWatch = StopWatch.create(watchName);

        stopWatch.start("获取报名采集字段");
        // 获取报名采集字段
        List<ProjectCollectInfoOption> officeProjectCollectInfoOption = iProjectCollectInfoOptionService.queryByProjectId(projectId, ComponentNameEnum.office.getCode());
        List<String> officeIds = officeProjectCollectInfoOption.stream()
                .map(ProjectCollectInfoOption::getOptionValue)
                .distinct()
                .collect(Collectors.toList());
        stopWatch.stop();

        stopWatch.start("装配报名基础统计项");
        List<ProjectStatistic> projectStatistics = this.assembleProjectStatistic(project, createDate, officeIds);
        projectStatisticService.saveBatch(projectStatistics);
        stopWatch.stop();

        log.debug("[报名统计][统计非区域权限报名]数据：{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
    }

    /**
     * 装配机构报名统计数据
     *
     * @param project    报名信息
     * @param createDate 创建时间
     * @param officeIds  机构ID集合
     * @return
     */
    private List<ProjectStatistic> assembleProjectStatistic(Project project, LocalDateTime createDate, List<String> officeIds) {
        if (CollUtil.isNotEmpty(officeIds)) {
            List<ProjectStatistic> projectStatistics = officeIds.stream()
                    .map(officeId -> assembleOfficeProjectStatistic(project, createDate, officeId))
                    .collect(Collectors.toList());
            return projectStatistics;
        }
        ProjectStatistic projectStatistic = assembleProjectStatistic(project, createDate);
        return Lists.newArrayList(projectStatistic);
    }

    /**
     * 装配机构报名统计数据
     *
     * @param project    报名信息
     * @param createDate 创建时间
     * @param officeId   部门ID
     * @return
     */
    private ProjectStatistic assembleOfficeProjectStatistic(Project project, LocalDateTime createDate, String officeId) {
        // 获取区域下已报名人数
        Integer enrolledNum = iStudentProjectServiceService.countEnrolledNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);
        // 获取区域下已报名人次
        Integer personTimeNum = iStudentSubjectServiceService.countPersonTimeNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);
        // 获取待审核人数
        Integer pendingNum = iStudentProjectServiceService.countPendingNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);
        // 获取审核通过人数
        Integer passAuditNum = iStudentProjectServiceService.countPassAuditNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);
        // 获取审核未通过人数
        Integer failAuditNum = iStudentProjectServiceService.countFailAuditNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);
        // 获取待缴费人数
        Integer unpaidNum = iStudentProjectServiceService.countUnpaidNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);
        // 获取已缴费人数
        Integer paidNum = iStudentProjectServiceService.countPaidNum(project.getId(), officeId)
                .orElse(CommonConstant.ZERO);

        ProjectStatistic projectStatistic = new ProjectStatistic()
                .setProjectId(project.getId())
                .setOfficeId(project.getOfficeId())
                .setDeptId(officeId)
                .setEnrolledNum(enrolledNum)
                .setPersonTimeNum(personTimeNum)
                .setPendingNum(pendingNum)
                .setPassAuditNum(passAuditNum)
                .setFailAuditNum(failAuditNum)
                .setUnpaidNum(unpaidNum)
                .setPaidNum(paidNum);
        projectStatistic.setCreateDate(createDate)
                .setUpdateDate(LocalDateTime.now())
                .setId(UUIDUtils.newSortUUID());
        return projectStatistic;
    }

    /**
     * 装配数据权限报名统计数据
     *
     * @param project    报名信息
     * @param createDate 创建时间
     * @param officeIds  机构ID集合
     * @return
     */
    private List<ProjectStatistic> assembleOfficeProjectStatistic(Project project, LocalDateTime createDate, List<String> officeIds) {
        // 如果存在机构采集信息，则以机构维度进行统计
        if (CollUtil.isNotEmpty(officeIds)) {
            List<ProjectStatistic> projectStatistics = officeIds.stream()
                    .map(officeId -> assembleOfficeProjectStatistic(project, createDate, officeId))
                    .collect(Collectors.toList());
            return projectStatistics;
        }
        // 没有机构统计信息根据区域统计走
        ProjectStatistic projectStatistic = assembleProjectStatistic(project, createDate);
        return Lists.newArrayList(projectStatistic);
    }

    /**
     * 装配数据权限报名统计数据
     *
     * @param project           报名信息
     * @param areaCode          区域code
     * @param areaCollectInfoId 区域采集信息ID
     * @param createDate        创建时间
     * @param officeIds         机构ID集合
     * @return
     */
    private List<ProjectStatistic> assemblePermissionProjectStatistic(Project project, String areaCode, String areaCollectInfoId, LocalDateTime createDate, List<String> officeIds) {
        // 如果存在机构采集信息，则以机构维度进行统计
        if (CollUtil.isNotEmpty(officeIds)) {
            List<ProjectStatistic> projectStatistics = officeIds.stream()
                    .map(officeId -> assembleAreaOfficeProjectStatistic(project, areaCode, officeId, areaCollectInfoId, createDate))
                    .collect(Collectors.toList());
            return projectStatistics;
        }
        // 没有机构统计信息根据区域统计走
        ProjectStatistic projectStatistic = assembleAreaProjectStatistic(project, areaCode, areaCollectInfoId, createDate);
        return Lists.newArrayList(projectStatistic);
    }

    /**
     * 装配统计区域机构报名统计数据
     *
     * @param project
     * @param areaCode
     * @param officeId
     * @param areaCollectInfoId
     * @param createDate
     * @return
     */
    private ProjectStatistic assembleAreaOfficeProjectStatistic(Project project, String areaCode, String officeId, String areaCollectInfoId, LocalDateTime createDate) {
        // 获取区域下已报名人数
        Integer enrolledNum = iStudentProjectServiceService.countAreaEnrolledNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);
        // 获取区域下已报名人次
        Integer personTimeNum = iStudentSubjectServiceService.countAreaPersonTimeNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);
        // 获取待审核人数
        Integer pendingNum = iStudentProjectServiceService.countAreaPendingNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);
        // 获取审核通过人数
        Integer passAuditNum = iStudentProjectServiceService.countAreaPassAuditNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);
        // 获取审核未通过人数
        Integer failAuditNum = iStudentProjectServiceService.countAreaFailAuditNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);
        // 获取待缴费人数
        Integer unpaidNum = iStudentProjectServiceService.countAreaUnpaidNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);
        // 获取已缴费人数
        Integer paidNum = iStudentProjectServiceService.countAreaPaidNum(project.getId(), areaCollectInfoId, areaCode, officeId)
                .orElse(CommonConstant.ZERO);

        ProjectStatistic projectStatistic = new ProjectStatistic()
                .setProjectId(project.getId())
                .setOfficeId(project.getOfficeId())
                .setDeptId(officeId)
                .setAreaCode(areaCode)
                .setEnrolledNum(enrolledNum)
                .setPersonTimeNum(personTimeNum)
                .setPendingNum(pendingNum)
                .setPassAuditNum(passAuditNum)
                .setFailAuditNum(failAuditNum)
                .setUnpaidNum(unpaidNum)
                .setPaidNum(paidNum);
        projectStatistic.setCreateDate(createDate)
                .setUpdateDate(LocalDateTime.now())
                .setId(UUIDUtils.newSortUUID());

        return projectStatistic;
    }

    @Override
    public void createProjectStatistic() {
        LocalDateTime endTime = LocalDateTimeUtil.offset(LocalDateTime.now(), configManager.getStatisticOffsetNumber(), ChronoUnit.DAYS);

        List<Project> projects = this.lambdaQuery()
                .eq(BaseEntity::getDelFlag, Boolean.FALSE)
                .le(Project::getBeginTime, LocalDateTime.now())
                .ge(Project::getEndTime, endTime)
                .orderByAsc(BaseEntity::getCreateDate)
                .list();
        log.debug("本次自动创建报名统计共{}个任务", projects.size());
        projects.stream()
                .map(BaseEntity::getId)
                .forEach(projectStatisticProducer::sendMessage);
    }

    @Override
    public void createProjectStatistic(String projectId) {
        List<Project> projects = this.lambdaQuery()
                .eq(BaseEntity::getDelFlag, Boolean.FALSE)
                .eq(StrUtil.isNotBlank(projectId), BaseEntity::getId, projectId)
                .orderByAsc(BaseEntity::getCreateDate)
                .list();
        log.debug("本次手动创建报名统计共{}个任务", projects.size());
        projects.stream()
                .map(BaseEntity::getId)
                .forEach(projectStatisticProducer::sendMessage);
    }

    @Override
    public void timeoutProjectStatistic() {
        List<Project> projects = this.lambdaQuery()
                .eq(BaseEntity::getDelFlag, Boolean.FALSE)
                .lt(Project::getEndTime, LocalDateTime.now())
                .orderByAsc(BaseEntity::getCreateDate)
                .list();
        log.debug("已经过期了的统计数据的报名共{}个任务", projects.size());
        projects.stream()
                .map(BaseEntity::getId)
                .forEach(projectStatisticProducer::sendMessage);
    }

    @Override
    public List<SimpleProjectVO> simpleProjectList(String officeId) {
        return this.lambdaQuery()
                .eq(BaseEntity::getDelFlag, Boolean.FALSE)
                .eq(Project::getStatus, BmProjectStateEnum.PUBLISHED.getState())
                .eq(StrUtil.isNotBlank(officeId), Project::getOfficeId, officeId)
                .orderByDesc(Project::getCreateDate)
                .list()
                .stream()
                .map(project -> BeanUtil.toBean(project, SimpleProjectVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public Project getProjectById(String projectId) {
        return Optional.ofNullable(getById(projectId))
                .orElseThrow(() -> InvalidRequestException.getException(31051601, "未查找到项目数据"));
    }

    /**
     * 装配区域下的报名统计数据
     *
     * @param project           报名信息
     * @param areaCode          区域code
     * @param areaCollectInfoId 区域报名采集信息ID
     * @param createDate        创建时间
     * @return
     */
    private ProjectStatistic assembleAreaProjectStatistic(Project project, String areaCode, String areaCollectInfoId, LocalDateTime createDate) {
        // 获取区域下已报名人数
        Integer enrolledNum = iStudentProjectServiceService.countAreaEnrolledNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);
        // 获取区域下已报名人次
        Integer personTimeNum = iStudentSubjectServiceService.countAreaPersonTimeNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);
        // 获取待审核人数
        Integer pendingNum = iStudentProjectServiceService.countAreaPendingNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);
        // 获取审核通过人数
        Integer passAuditNum = iStudentProjectServiceService.countAreaPassAuditNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);
        // 获取审核未通过人数
        Integer failAuditNum = iStudentProjectServiceService.countAreaFailAuditNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);
        // 获取待缴费人数
        Integer unpaidNum = iStudentProjectServiceService.countAreaUnpaidNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);
        // 获取已缴费人数
        Integer paidNum = iStudentProjectServiceService.countAreaPaidNum(project.getId(), areaCollectInfoId, areaCode, null)
                .orElse(CommonConstant.ZERO);

        ProjectStatistic projectStatistic = new ProjectStatistic()
                .setProjectId(project.getId())
                .setOfficeId(project.getOfficeId())
                .setDeptId(project.getDeptId())
                .setAreaCode(areaCode)
                .setEnrolledNum(enrolledNum)
                .setPersonTimeNum(personTimeNum)
                .setPendingNum(pendingNum)
                .setPassAuditNum(passAuditNum)
                .setFailAuditNum(failAuditNum)
                .setUnpaidNum(unpaidNum)
                .setPaidNum(paidNum);
        projectStatistic.setCreateDate(createDate)
                .setUpdateDate(LocalDateTime.now())
                .setId(UUIDUtils.newSortUUID());

        return projectStatistic;
    }

    /**
     * 装配无区域权限下的报名统计数据
     *
     * @param project
     * @return
     */
    private ProjectStatistic assembleProjectStatistic(Project project, LocalDateTime createDate) {
        // 获取区域下已报名人数
        Integer enrolledNum = iStudentProjectServiceService.countEnrolledNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);
        // 获取区域下已报名人次
        Integer personTimeNum = iStudentSubjectServiceService.countPersonTimeNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);
        // 获取待审核人数
        Integer pendingNum = iStudentProjectServiceService.countPendingNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);
        // 获取审核通过人数
        Integer passAuditNum = iStudentProjectServiceService.countPassAuditNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);
        // 获取审核未通过人数
        Integer failAuditNum = iStudentProjectServiceService.countFailAuditNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);
        // 获取待缴费人数
        Integer unpaidNum = iStudentProjectServiceService.countUnpaidNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);
        // 获取已缴费人数
        Integer paidNum = iStudentProjectServiceService.countPaidNum(project.getId(), null)
                .orElse(CommonConstant.ZERO);

        ProjectStatistic projectStatistic = new ProjectStatistic()
                .setProjectId(project.getId())
                .setOfficeId(project.getOfficeId())
                .setDeptId(project.getDeptId())
                .setEnrolledNum(enrolledNum)
                .setPersonTimeNum(personTimeNum)
                .setPendingNum(pendingNum)
                .setPassAuditNum(passAuditNum)
                .setFailAuditNum(failAuditNum)
                .setUnpaidNum(unpaidNum)
                .setPaidNum(paidNum);
        projectStatistic.setCreateDate(createDate)
                .setUpdateDate(LocalDateTime.now())
                .setId(UUIDUtils.newSortUUID());
        return projectStatistic;
    }

    /**
     * 校验项目名称是否存在
     * @see com.hyt.it.ogt.kq.service.bm.service.IProjectService#checkProjectNameExits(java.lang.String, java.lang.String)
     **/
    @Override
    public Boolean checkProjectNameExits(String projectName, String officeId) {
        QueryWrapper<Project> projectQueryWrapper = new QueryWrapper<>();
        projectQueryWrapper.lambda().eq(Project::getOfficeId, officeId).eq(Project::getProjectName, projectName).eq(Project::getDelFlag, DelFlagEnum.NORMAL.getCode());
        if(count(projectQueryWrapper) > 0) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * 根据项目名称获取正在开放的报名任务
     * @see com.hyt.it.ogt.kq.service.bm.service.IProjectService#getOpenProjectByName(java.lang.String, java.lang.String)
     **/
    @Override
    public Project getOpenProjectByName(String officeId) {
        QueryWrapper<Project> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
            .eq(Project::getOfficeId, officeId)
            .eq(Project::getDelFlag, Boolean.FALSE)
            .eq(Project::getStatus, BmProjectStateEnum.PUBLISHED.getState())
            .lt( Project::getBeginTime, LocalDateTime.now())
            .gt( Project::getEndTime, LocalDateTime.now())
            .orderByAsc(Project::getCreateDate).last("limit 1");
        return getOne(queryWrapper);
    }
}
